using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
using IsometricEngine;
using Fated.SaveEngine;
using Fated.Character;
using Fated.BattleEngine;
using Fated.Scenario.Events;

namespace Fated.Scenario {
    public class Scenario : Renderable, IJSON {
        const string ScenesFolder = "Scenes/";
        const string MapsFolder = "Maps/";

        ContentManager content;
        private SceneData currentScene;

        private string currentSceneName;
        private int currentEvent;
        private Renderable curEventObj;
        private Random randomNumbers = new Random();

        private long leftStickIndex, rightStickIndex, aButtonIndex;

        public Scenario(ContentManager cM, string filename) {
            content = cM;
            currentSceneName = filename;
            currentEvent = 0;

            curEventObj = new Renderable();
            this.children.Add(curEventObj);

            curEventObj.enabled = false;

            loadScene(filename);

            this.renderType = RenderType.None;
            this.enabled = true;

            leftStickIndex = TimingMngr.registerControl();
            rightStickIndex = TimingMngr.registerControl();
            aButtonIndex = TimingMngr.registerControl();
            /*
             * This.textout / this.texture can be used to display dialogue, messages, etc...
             * Remember to set this.RenderType = None when not in use.
             */
        }
        public Scenario(ContentManager cM, Dictionary<string, object> data)
            : this(cM, (string)data["currentscenename"]) {
            double number = (double)data["currentevent"];
            nextEvent((int)number);
        }

        public void update(double elapsedTime, InputState newS) {

            if (curEventObj.GetType() == typeof(MessageBox)) {
                if (TimingMngr.tickAvailable(newS.PrimaryStick, leftStickIndex, elapsedTime))
                    ((MessageBox)curEventObj).updateText(newS.PrimaryStick);

            }

            if (currentScene.events[currentEvent].GetType() == typeof(Battle)) {
                BattleInterface.update(elapsedTime, newS);

            } else if (curEventObj.GetType() == typeof(ChoiceScreen)) {
                ChoiceScreen tempCS = (ChoiceScreen)curEventObj;
                if (TimingMngr.tickAvailable(newS.PrimaryStick, leftStickIndex, elapsedTime))
                    tempCS.updateSelection(newS.PrimaryStick);

                if (TimingMngr.tickAvailable(newS.SecondaryStick, rightStickIndex, elapsedTime))
                    tempCS.updateText(newS.SecondaryStick);

                if (newS.UniqueSelectButtonPress) {
                    TimingMngr.resetTickTime(rightStickIndex);
                    nextEvent(tempCS.getSelected());
                }

            } else if (newS.UniqueSelectButtonPress) {
                nextEvent();
            }
            if (newS.UniqueMenuButtonPress) {
                SaveMngr.saveIJSONToFile(this, SaveFile.Scenario);
                SaveMngr.saveDataToFile(CurrentCharacter.Data, SaveFile.Character);
            }
        }

        private void nextEvent(string label) {
            for (int i = 0; i < currentScene.events.Count(); i++) {
                if (currentScene.events[i].GetType() == typeof(Destination)) {
                    if (((Destination)currentScene.events[i]).label == label) {
                        nextEvent(i + 1);
                        break;
                    }
                }
            }
        }
        private void nextEvent() {
            currentEvent++;
            loadEvent();
        }
        private void nextEvent(int n) {
            currentEvent = n;
            loadEvent();
        }

        private void loadEvent() {
            // Note: Either curEventObj should be set and added or
            // nextEvent should be called by the end of this function.
            Event curEventData = currentScene.events[currentEvent];
            Type eventType = curEventData.GetType();
            children.Remove(curEventObj);

            if (eventType == typeof(Dialogue)) {
                curEventObj = new DialogueBox((Dialogue)curEventData);
            } else if (eventType == typeof(Message)) {
                curEventObj = new MessageBox((Message)curEventData);
            } else if (eventType == typeof(Cards)) {
                curEventObj = new CardScreen((Cards)curEventData);
            } else if (eventType == typeof(Choice)) {
                TimingMngr.setTickTime(rightStickIndex, .1);
                curEventObj = new ChoiceScreen((Choice)curEventData);
            } else if (eventType == typeof(ChangeScene)) {
                // Totes includes nextEvent
                loadScene(((ChangeScene)curEventData).destination);
                return;
            } else if (eventType == typeof(Battle)) {
                // Should not have nextEvent. It will happen in update.
                Battle battleData = (Battle)curEventData;
                BattleInterface.startBattle(battleData.battlename);
                return;
            } else if (eventType == typeof(GotoDestination)) {
                nextEvent(((GotoDestination)curEventData).label);
                return;
            } else if (eventType == typeof(AddClass)) {
                CurrentCharacter.AddClass(((AddClass)curEventData).name);
                nextEvent();
                return;
            } else if (eventType == typeof(SetClass)) {
                CurrentCharacter.SetClass(((SetClass)curEventData).name);
                nextEvent();
                return;
            } else if (eventType == typeof(RemoveClass)) {
                CurrentCharacter.RemoveClass(((RemoveClass)curEventData).name);
                nextEvent();
                return;
            } else if (eventType == typeof(IfGotoElseGoto)) {
                // Includes nextEvent
                handleIfElseEvent((IfGotoElseGoto)curEventData);
                return;
            } else if (eventType == typeof(RandomDestination)) {
                // Includes nextEvent
                handleRandomEvent((RandomDestination)curEventData);
                return;
            } else if (eventType == typeof(Gift)) {
                handleGiftEvent((Gift)curEventData);
                nextEvent();
                return;
            }

            children.Add(curEventObj);
        }

        private void handleRandomEvent(RandomDestination eve) {
            eve.normalizeProbabilities();
            double randomNumber = randomNumbers.NextDouble();
            double newSum = 0;
            foreach (RandomDestination.WeightedDestination w in eve.destinations) {
                if (newSum < randomNumber && newSum + w.probability > randomNumber) {
                    nextEvent(w.label);
                    break;
                } else {
                    newSum += w.probability;
                }
            }
        }
        private void handleIfElseEvent(IfGotoElseGoto e) {
            string[] split = e.condition.Split(new char[] { ',', ' ', '.' });
            double param1 = 0, param2 = 0;
            // ------------------------- Get Param 1
            if (split[0].ToLower().CompareTo("player") == 0) {
                param1 = CurrentCharacter.getAttribute(split[1].ToLower());
            }//else if (ad inifinitum)

            // ------------------------- Get Param 2
            if (split[3].ToLower().CompareTo("player") == 0) {
                param2 = CurrentCharacter.getAttribute(split[4].ToLower());
            }//else if (ad inifinitum)

            // ------------------------- Compare
            if (split[2] == ">") {
                if (param1 > param2) nextEvent(e.truecase); else nextEvent(e.falsecase);
                return;
            } else if (split[2] == "<") {
                if (param1 < param2) nextEvent(e.truecase); else nextEvent(e.falsecase);
                return;
            } else if (split[2] == "=") {
                if (Math.Abs(param1 - param2) < .0001) nextEvent(e.truecase); else nextEvent(e.falsecase);
                return;
            }
            throw new Exception("Invalid Conditional String");
        }
        private void handleGiftEvent(Gift g) {
            if (g.type == Gift.GiftType.Gold) {
                CurrentCharacter.addGold(g.amount);
            } else if (g.type == Gift.GiftType.Item) {
                CurrentCharacter.addItem(g.item, g.amount);
            }
        }

        private void loadScene(string filename) {
            currentScene = content.Load<SceneData>(ScenesFolder + filename);
            currentSceneName = filename;

            //EngineInterface.loadMap(MapsFolder + currentScene.MapName);

            foreach (SpriteLocation l in currentScene.locations) {
                if (l.charName.CompareTo("player") == 0) {
                    //CurrentCharacter.setPosition(l.x, l.y);
              //      EngineInterface.addSpriteAnimation("player",
                //        FatedGame.currentCharacter);
                }
            }

            nextEvent(0);
        }

        public Dictionary<string, object> ToJSON() {
            Dictionary<string, object> output = new Dictionary<string, object>();
            output.Add("currentscenename", currentSceneName);
            output.Add("currentevent", currentEvent);
            return output;
        }
    }
}
